using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using System.IO;
using System.Diagnostics;
using ScintillaNET;
using KRBTabControl;

namespace FizzDevelop
{
    public partial class MainWin : Form
	{
		#region Constructor, Overrides and Variables

		private int newFiles = 0;
		private List<FizzFileTab> FileTabs = new List<FizzFileTab>();
		private List<FizzProject> Projects = new List<FizzProject>();
		
		public FizzTab CurrentTab
		{
			get { return ((FizzTab)MainTabControl.SelectedTab); }
		}

		public MainWin()
        {
            InitializeComponent();
			this.SuspendLayout();
			List<ToolStripItem> items = new List<ToolStripItem>();
            foreach (LanguageInfo langauge in Languages.LanguageList.Values)
			{
				ToolStripMenuItem item = new ToolStripMenuItem(langauge.DisplayName);
				items.Add(item);
			}
			items.Sort(Languages.CompareLanguagesByTitle);
			LanguageDropDown.DropDownItems.AddRange(items.ToArray());
			/*
			foreach (KeyValuePair<string, ExternalTool> tool in ExternalTools.ToolList)
			{
				ToolStripMenuItem item = new ToolStripMenuItem(tool.Key);
				OpenInCombo.DropDownItems.Add(item);
			}
			*/
			MainSplit.Panel1Collapsed = true;
			NewFileTab();
			/*
			StartPage StartPage = new StartPage();
			StartTab.Text = "Start Page";
			StartTab.Item = StartPage;
			StartPage.Dock = DockStyle.Fill;
			StartTab.Controls.Add(StartPage);
			MainTabControl.Controls.Add(StartTab);
			*/

			MainTabControl.Selected += new TabControlEventHandler(Tabs_Selected);
			this.ResumeLayout();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
                case (Keys.Control | Keys.C):
					EditCopy();
                    return true;
                case (Keys.Control | Keys.X):
                    EditCut();
                    return true;
                case (Keys.Control | Keys.V):
                    EditPaste();
                    return true;
				case (Keys.Control | Keys.O):
					OpenFile();
					return true;
				case (Keys.Control | Keys.S):
					SaveToolbarButton.Enabled = !((FizzFileTab)MainTabControl.SelectedTab).Save();
					SaveMenuButton.Enabled = SaveAllToolbarButton.Enabled;
					return true;
				case (Keys.Control | Keys.N):
					NewFileTab();
					return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

		#endregion

		#region File Methods

		private void OpenFile()
		{
			string path;
			OpenFileDialog filedialog = new OpenFileDialog();
			filedialog.Title = "Open file...";
			if (filedialog.ShowDialog() == DialogResult.OK) path = filedialog.FileName;
			else return;
			FizzFileTab tab;
			if (CurrentTab.Text == "File1" && ((FizzFileTab)CurrentTab).isNewFile)
				tab = (FizzFileTab)CurrentTab;
			else
				tab = NewFileTab();
			tab.OpenFile(path);
			Tab_Selected(tab);
			UpdateProjects();
		}

		private FizzFileTab NewFileTab()
		{
			FizzFileTab tab = new FizzFileTab();
			this.FileTabs.Add(tab);
			this.newFiles++;
			tab.Text = "File" + newFiles;
			FizzCodeEditor editor = new FizzCodeEditor(tab);
			editor.SelectionChanged += new EventHandler(Editor_SelectionChanged);
			editor.TextInserted += editor_TextInserted;
			editor.TextDeleted += editor_TextDeleted;
			MainTabControl.TabPages.Add(tab);
			MainTabControl.SelectTab(tab);
			Tab_Selected(tab);
			UpdateProjects();
			return tab;
		}

		#endregion

		#region GUI Methods

		private void UpdateLanguage(Language lang)
		{
			LanguageDropDown.Text = Languages.LanguageList[lang].DisplayName;
			if (lang == Language.Plaintext)
			{
				FoldingToggle.Checked = false;
				FoldingToggle.Enabled = false;
				RunToolbar.Visible = false;
			}
			else
			{
				FoldingToggle.Checked = true;
				FoldingToggle.Enabled = true;
				RunPlatformCombo.DropDownItems.Clear();
				// Note: This code is to enable the run toolbar. TODO: Rewrite with LanguageList when there's time.
				if (Languages.LanguageList[lang].Platforms != null)
				{
					RunToolbar.Visible = true;
					RunPlatformCombo.Text = Languages.LanguageList[lang].Platforms[0].DisplayName;
					foreach (Platform platform in Languages.LanguageList[lang].Platforms)
					{
						ToolStripMenuItem item = new ToolStripMenuItem(platform.DisplayName);
						RunPlatformCombo.DropDownItems.Add(item);
					}
					if (((FizzFileTab)CurrentTab).BinaryFiles != null)
						RunToolbarButton.Enabled = true;
					else
						RunToolbarButton.Enabled = false;
				}
				else
					RunToolbar.Visible = false;
			}
		}

		private void UpdateSelection(object sender)
		{
			FizzCodeEditor editor = (FizzCodeEditor)sender;
			LineNumberStatus.Text = "Ln: " + (editor.Caret.LineNumber + 1);
			ColumnNumberStatus.Text = "Col: " + (editor.GetColumn(editor.CurrentPos) + 1);
			SelectionLengthStatus.Text = "Sel: " + editor.Selection.Length;
			CopyToolbarButton.Enabled = editor.Clipboard.CanCopy;
			CopyMenuButton.Enabled = editor.Clipboard.CanCopy;
			CutToolbarButton.Enabled = editor.Clipboard.CanCut;
			CutMenuButton.Enabled = editor.Clipboard.CanCut;
			PasteToolbarButton.Enabled = editor.Clipboard.CanPaste;
			PasteMenuButton.Enabled = editor.Clipboard.CanPaste;
			UndoToolbarButton.Enabled = editor.UndoRedo.CanUndo;
			RedoToolbarButton.Enabled = editor.UndoRedo.CanRedo;
			UndoMenuButton.Enabled = editor.UndoRedo.CanUndo;
			RedoMenuItem.Enabled = editor.UndoRedo.CanRedo;
			SaveToolbarButton.Enabled = !editor.Tab.Saved;
			SaveMenuButton.Enabled = !editor.Tab.Saved;
			if (editor.Tab.File == null)
				if (editor.Tab.Saved) SaveToolbarButton.Enabled = true;
			bool allSaved = true;
			foreach (FizzFileTab tab in FileTabs)
			{
				if (!tab.Saved || tab.isNewFile)
					allSaved = false;
			}
			SaveAllToolbarButton.Enabled = !allSaved;
			SaveAllMenuButton.Enabled = SaveAllToolbarButton.Enabled;
			LineNumberToggle.Checked = editor.ShowLineNumbers;
			FoldingToggle.Checked = editor.ShowFolding;
			LineWrappingToggle.Checked = editor.DoLineWrapping;
			LineNumberMenuToggle.Checked = editor.ShowLineNumbers;
			FoldingMenuToggle.Checked = editor.ShowFolding;
			LineWrappingMenuToggle.Checked = editor.DoLineWrapping;
			ShowSidePanelMenuButton.Checked = !MainSplit.Panel1Collapsed;
			addFileToProjectToolStripMenuItem.Enabled = ((FizzFileTab)CurrentTab).File != null;
		}

		public void UpdateProjects()
		{
			ProjectTree.Nodes.Clear();
			TreeNode noProject = new TreeNode("(No Project)");
			foreach (FizzProject project in Projects)
			{
				TreeNode projectNode = new TreeNode(project.DisplayName);
				if (project.Files != null && project.Files.Count > 0)
				{
					foreach (FizzFile file in project.Files)
					{
						TreeNode fileNode = new TreeNode(Path.GetFileName(file.Path));
						projectNode.Nodes.Add(fileNode);
					}
				}
				else
				{
					projectNode.Nodes.Add(new TreeNode("(No Files)"));
				}
				ProjectTree.Nodes.Add(projectNode);
			}
			foreach (FizzFileTab tab in FileTabs)
			{
				if (tab.File != null)
				{
					if (tab.File.Project == null)
					{
						TreeNode fileNode = new TreeNode(tab.Text);
						noProject.Nodes.Add(fileNode);
					}
				}
				else
				{
					TreeNode fileNode = new TreeNode(tab.Text);
					noProject.Nodes.Add(fileNode);
				}
			}
			if (noProject.Nodes.Count > 0)
				ProjectTree.Nodes.Add(noProject);
			ProjectTree.ExpandAll();
		}

		#endregion

		#region Editing

		private void EditCopy()
		{
			FizzCodeEditor editor = (FizzCodeEditor)((FizzFileTab)CurrentTab).Editor;
			editor.Clipboard.Copy();
		}

		private void EditCut()
		{
			FizzCodeEditor editor = (FizzCodeEditor)((FizzFileTab)CurrentTab).Editor; 
			editor.Clipboard.Cut();
		}

		private void EditPaste()
		{
			FizzCodeEditor editor = (FizzCodeEditor)((FizzFileTab)CurrentTab).Editor; 
			editor.Clipboard.Paste();
		}

		private void SelectAll(Scintilla editor)
		{
			editor.Selection.SelectAll();
		}

		#endregion

		#region Events

		private void NewProjectToolbarButton_Click(object sender, EventArgs e)
		{
			NewProjectDialog dialog = new NewProjectDialog();
			string path;
			string name;
			DialogResult result = dialog.ShowDialog(out name, out path);
			if (result == DialogResult.OK)
			{
				FizzProject project = new FizzProject(name, path);
				project.Create();
				Projects.Add(project);
				UpdateProjects();
				MainSplit.Panel1Collapsed = false;
			}
			/*
			FolderSelect.FolderSelectDialog dialog = new FolderSelect.FolderSelectDialog();
			dialog.Title = "Create new project...";
			if (dialog.ShowDialog())
			{
				string name = new DirectoryInfo(dialog.FileName).Name;
				FizzProject project = new FizzProject(name, Path.GetDirectoryName(dialog.FileName));
				project.Create();
				Projects.Add(project);
				UpdateProjects();
			}
			*/
		}

		private void showSidePanelToolStripMenuItem_Click(object sender, EventArgs e)
		{
			MainSplit.Panel1Collapsed = !((ToolStripMenuItem)sender).Checked;
		}

		private void MainWin_FormClosing(object sender, FormClosingEventArgs e)
		{
			List<FizzFileTab> notSaved = FileTabs.Where(item => !item.Saved).ToList();
			if (notSaved.Count > 0)
			{
				using (SaveDialog dialog = new SaveDialog(notSaved))
				{
					CheckedListBox.CheckedItemCollection checkedFiles;
					DialogResult result = dialog.ShowDialog(out checkedFiles);

					switch (result)
					{
						case DialogResult.Yes:
							foreach (string tabText in checkedFiles)
							{
								FizzFileTab tab = FileTabs.Find(delegate(FizzFileTab item) { return item.Text == tabText; });
								tab.Save();
							}
							break;
						case DialogResult.No:
							break;
						case DialogResult.Cancel:
							e.Cancel = true;
							break;
					}
				}
			}
		}

		private void Undo_Click(object sender, EventArgs e)
		{
			FizzFileTab tab = (FizzFileTab)MainTabControl.SelectedTab;
			((FizzCodeEditor)tab.Editor).UndoRedo.Undo();
		}

		private void Redo_Click(object sender, EventArgs e)
		{
			FizzFileTab tab = (FizzFileTab)MainTabControl.SelectedTab;
			((FizzCodeEditor)tab.Editor).UndoRedo.Redo();
		}

		private void NewFileClick(object sender, EventArgs e)
		{
			NewFileTab();
		}

		private void about_Click(object sender, EventArgs e)
		{
			AboutFizzEdit about = new AboutFizzEdit();
			about.ShowDialog();
		}

		private void Tabs_Selected(object sender, TabControlEventArgs e)
		{
			Tab_Selected((FizzFileTab)e.TabPage);
		}

		private void Tab_Selected(FizzFileTab tab)
		{
			switch (tab.Editor.GetType().ToString())
			{
				case "FizzDevelop.FizzCodeEditor":
					FizzCodeEditor editor = (FizzCodeEditor)tab.Editor;
					editor.Select();
					UpdateLanguage(editor.Language);
					UpdateSelection(editor);
					break;
				default:
					break;
			}
		}

		private void LanguageDropDown_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
		{
			ToolStripMenuItem item = (ToolStripMenuItem)e.ClickedItem;
			Language language = Languages.LanguageList.Keys.ToList().Find(delegate(Language lang) { return Languages.LanguageList[lang].DisplayName == item.Text; });
			((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).Language = language;
			UpdateLanguage(language);
		}

		private void Open_Click(object sender, EventArgs e)
		{
			OpenFile();
		}

		private void Save_Click(object sender, EventArgs e)
		{
			SaveToolbarButton.Enabled = !((FizzFileTab)CurrentTab).Save();
			UpdateSelection((object)((FizzFileTab)CurrentTab).Editor);
		}

		private void SaveAll_Click(object sender, EventArgs e)
		{
			foreach (FizzFileTab tab in MainTabControl.TabPages)
			{
				tab.Save();
			}
			Tab_Selected((FizzFileTab)CurrentTab);
		}

		private void SaveAs_Click(object sender, EventArgs e)
		{
			SaveToolbarButton.Enabled = !((FizzFileTab)CurrentTab).SaveAs();
			UpdateSelection((object)((FizzFileTab)CurrentTab).Editor);
		}

		private void EditCopy_Click(object sender, EventArgs e)
		{
			EditCopy();
		}

		private void EditCut_Click(object sender, EventArgs e)
		{
			EditCut();
		}

		private void EditPaste_Click(object sender, EventArgs e)
		{
			EditPaste();
		}

		private void Tabs_TabPageClosing(object sender, KRBTabControl.KRBTabControl.SelectedIndexChangingEventArgs e)
		{
			if (((FizzFileTab)e.TabPage).Saved == false)
			{
				DialogResult save = MessageBox.Show("Do you want to save " + ((FizzFileTab)e.TabPage).Text + "?", "Save File?", MessageBoxButtons.YesNoCancel);
				if (save == System.Windows.Forms.DialogResult.Cancel) e.Cancel = true;
				else 
				{
					if (save == System.Windows.Forms.DialogResult.Yes)
						((FizzFileTab)e.TabPage).Save();
					FileTabs.Remove((FizzFileTab)e.TabPage);
					UpdateProjects();
				}
			}
			else
			{
				FileTabs.Remove((FizzFileTab)e.TabPage);
				UpdateProjects();
			}
		}

		private void SelectAll_Click(object sender, EventArgs e)
		{
			FizzCodeEditor editor = (FizzCodeEditor)((FizzFileTab)MainTabControl.SelectedTab).Editor;
			SelectAll(editor);
		}
		void editor_TextDeleted(object sender, TextModifiedEventArgs e)
		{
			Editor_TextChanged(sender);
		}

		void editor_TextInserted(object sender, TextModifiedEventArgs e)
		{
			Editor_TextChanged(sender);
		}

		void Editor_TextChanged(object sender)
		{
			FizzFileTab tab = ((FizzCodeEditor)sender).Tab;
			tab.Saved = false;
			FizzCodeEditor editor = (FizzCodeEditor)sender;
			int digits = editor.Lines.Count.ToString().Length;
			editor.Margins.Margin0.Width = digits * 10 + (1 / digits * 5);
		}

		void Editor_SelectionChanged(object sender, EventArgs e)
		{
			UpdateSelection(sender);
		}

		private void LineNumberToggle_Click(object sender, EventArgs e)
		{
			if (((dynamic)sender).Checked)
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).ShowLineNumbers = true;
			else
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).ShowLineNumbers = false;
			UpdateSelection(((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor));
		}

		private void FoldingToggle_Click(object sender, EventArgs e)
		{
			if (((dynamic)sender).Checked)
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).ShowFolding = true;
			else
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).ShowFolding = false;
			UpdateSelection(((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor));
		}

		private void LineWrappingToggle_Click(object sender, EventArgs e)
		{
			if (((dynamic)sender).Checked)
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).DoLineWrapping = true;
			else
				((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor).DoLineWrapping = false;
			UpdateSelection(((FizzCodeEditor)((FizzFileTab)CurrentTab).Editor));
		}

		private void RunToolbarButton_Click(object sender, EventArgs e)
		{
			StatusbarStatusText.Text = "Starting...";
			FizzFileTab tab = (FizzFileTab)CurrentTab;
			Language lang = ((FizzCodeEditor)tab.Editor).Language;
			switch (Languages.LanguageList[lang].Platforms[0].Type)
			{
				case PlatformType.Compiled:
					Process.Start(tab.BinaryFiles[0]);
					break;
				case PlatformType.Managed:
					Languages.LanguageList[lang].Platforms[0].Run(tab.BinaryFiles, null);
					break;
				case PlatformType.Interpreted:
					break;
				default:
					break;
			}
			StatusbarStatusText.Text = "Ready";
		}

		private void CompileToolbarButton_Click(object sender, EventArgs e)
		{
			StatusbarStatusText.Text = "Building...";
			InfoBox message = InfoBox.ShowMessage("Building file(s)...", "Building");
			FizzCodeEditor editor = (FizzCodeEditor)((FizzFileTab)CurrentTab).Editor;
			Language lang = editor.Language;
			BuildStatus status = Languages.LanguageList[lang].Platforms[0].Build(new List<string> { ((FizzFileTab)CurrentTab).File.Path }, null);
			message.Close();
			switch (status.Success)
			{
				case Success.Successful:
					((FizzFileTab)CurrentTab).BinaryFiles = status.PreparedFiles;
					RunToolbarButton.Enabled = true;
					break;
				case Success.Failed:
					MessageBox.Show("An error occured in the build process. Compiler output is provided below.\n\nStandard Output Stream:\n " + status.Output + "\n\nStandard Error Stream:\n" + status.Error, "Build Error");
					break;
				case Success.Aborted:
					break;
			}
			StatusbarStatusText.Text = "Ready";
		}
		
		private void OnlineHelpMenuButton_Click(object sender, EventArgs e)
		{
			Process.Start("http://fizzdevelop.codeplex.com/documentation");
		}

		private void addFileToProjectToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SelectFromListDialog dialog = new SelectFromListDialog(Projects);
			dynamic project;
			if (dialog.ShowDialog(out project) == DialogResult.OK)
			{
				project.Files.Add(((FizzFileTab)CurrentTab).File);
				((FizzFileTab)CurrentTab).File.Project = project;
				UpdateProjects();
			}
		}

		#endregion
	}
}
